/**
 * linkrfi.cpp
 *
 * data link layer radio frequency interface.
 *
 * Copyright (c) sti-seedclass 2011, tankery.chen@gmail.com
 */
#include <stdlib.h>
#include <string.h>
#include <LPC11xx.h>
#include "utilfun.h"
#include "halrfi.h"
#include "linkrfi.h"
#include "linkrfiframe.h"

#define BITSPEED    (1000000L/BANDRATE)
#define SLOTS       (1* (ACK_FRAME_SIZE*BITSPEED) )
#define SIFS        (1*BITSPEED)
#define DIFS        (1* (2*SLOTS + SIFS) )
#define EIFS        (1* ((DATA_FRAME_SIZE + ACK_FRAME_SIZE)*BITSPEED + SIFS) )

enum WorkingState {
    LINK_RFI_SLEEP = 0,
    LINK_RFI_WORKING
};

static uint32_t src_addr;            // Source address.
static uint32_t linkState;           // link layer working state.
static uint8_t seqSend, seqRecv;    // Sequence numbers.

static volatile uint8_t receiveBuffer[DATA_FRAME_SIZE];
static volatile uint32_t bufOffset;

static uint32_t rfiDataRdy;
static uint32_t rfiAckRdy;
static uint32_t rfiChannelBusy;

static callback_array upLayerReceivedFunc;


static void sendAck(uint8_t addr);
static uint32_t waitForAck(uint32_t timeout);
static uint32_t channelIdle(void);
static uint32_t CRCCheckOK(uint8_t *data, uint32_t len, uint8_t CRCData);
static uint8_t makeCRCByte(uint8_t *data, uint32_t len);
static void dataReceived(void *);

static void dataReceived(void * null)
{
	volatile uint8_t data[DATA_FRAME_SIZE*2];
    volatile uint32_t len;
    volatile uint8_t * header = data;
    volatile uint32_t restLen, restOffset;

    // has data received, so channel is busy.
    rfiChannelBusy = 1;

    if (!halrfiDataRdy()) {
        return;
    }
	// if buffer full, stop receive and return.
	if (bufOffset > 1) {
		halrfiRxOff();
		return;
	}
    
	len = halrfiReceive((uint8_t*)data);

    // if receive buffer empty, ignore none header.
    if (bufOffset == 0 && len == 1) {
        if (LINK_RFI_FRAME_TYPE_BYTE(header) != LINK_RFI_DATA &&
                LINK_RFI_FRAME_TYPE_BYTE(header) != LINK_RFI_ACK) {
            // no header word in current array give up.
            return;
        }
        // found header, cpy it.
        receiveBuffer[0] = header[0];
        header++;
        bufOffset++;
        len = data + len - header;

	    if (receiveBuffer[0] == LINK_RFI_DATA) {
	        halrfiSetRdySize(DATA_FRAME_SIZE - 1);
	    }
	    else if (receiveBuffer[0] == LINK_RFI_ACK) {
	        halrfiSetRdySize(ACK_FRAME_SIZE - 1);
	    }
		
    }
	else if (bufOffset == 1) {
        // receive buffer full, close receive exchange and process data.
		halrfiRxOff();

	    memcpy((uint8_t*)receiveBuffer + bufOffset, (uint8_t*)header, len);
	    bufOffset += len;

        if (LINK_RFI_FRAME_TYPE_BYTE(receiveBuffer) == LINK_RFI_ACK &&
			len == (ACK_FRAME_SIZE - 1) &&
			LINK_RFI_FRAME_DES_BYTE(receiveBuffer) == src_addr) {
            rfiDataRdy = 0;
            rfiAckRdy = 1;
            seqRecv = LINK_RFI_FRAME_SEQ_BYTE(receiveBuffer);
        }
        else if (LINK_RFI_FRAME_TYPE_BYTE(receiveBuffer) == LINK_RFI_DATA &&
			len == (DATA_FRAME_SIZE - 1) &&
			(LINK_RFI_FRAME_DES_BYTE(receiveBuffer) == src_addr
			|| LINK_RFI_FRAME_DES_BYTE(receiveBuffer) == 0xff)     // broadcast address.
			&& LINK_RFI_FRAME_SRC_BYTE(receiveBuffer) != src_addr	// not frame from myself.
            // CRC error check.
            //&& CRCCheckOK((uint8_t*)receiveBuffer, DATA_FRAME_SIZE - 1, receiveBuffer[DATA_FRAME_SIZE - 1])
			) {
            rfiDataRdy = 1;
            rfiAckRdy = 0;
            seqRecv = LINK_RFI_FRAME_SEQ_BYTE(receiveBuffer);
            sendAck(LINK_RFI_FRAME_SRC_BYTE(receiveBuffer));
        }
		// bad or else node frame, free it.
		else {
            rfiDataRdy = 0;
            rfiAckRdy = 0;
			bufOffset = 0;
			memset((uint8_t*)receiveBuffer, 0, DATA_FRAME_SIZE);
			halrfiRxOn();
		}
		halrfiSetRdySize(1);
    }
}

static uint32_t CRCCheckOK(uint8_t *data, uint32_t len, uint8_t CRCData)
{
    return (makeCRCByte(data, len) == CRCData);
}

static uint8_t makeCRCByte(uint8_t *data, uint32_t len)
{
    uint32_t i;
    uint8_t crc = 0;
    for (i = 0; i < len; ++i) {
        crc ^= data[i];
    }
    
    return crc;
}

static void sendAck(uint8_t addr)
{
	volatile uint8_t buf[ACK_FRAME_SIZE];
    volatile uint32_t len;

    uint32_t busyRetrans = 4;
    uint32_t BW = 32;
    uint32_t busytime;

    LINK_RFI_FRAME_TYPE_BYTE(buf) = LINK_RFI_ACK;
    LINK_RFI_FRAME_SEQ_BYTE(buf) = seqRecv;
    LINK_RFI_FRAME_DES_BYTE(buf) = addr;
    do {
        if (channelIdle()) {
			halrfiTxOn();
            halrfiSend((uint8_t*)buf, ACK_FRAME_SIZE);
			halrfiRxOn();
            break;
        }
        else {
            // backoff random time based on DIFS.
            busytime = rand() % BW;
            busytime *= DIFS;
            utilSleepUs(busytime);
            BW <<= 1;
        }
    } while (--busyRetrans != 0);
}

static uint32_t waitForAck(uint32_t timeout)
{
    do {
        if (rfiAckRdy) {
			rfiAckRdy = 0;
			bufOffset = 0;
            if (seqRecv == seqSend) {
				// reopen receive exchange.
				halrfiRxOn();
                return 1;
			}
			halrfiRxOn();
        }
        utilDelayMs(1);
    } while (--timeout != 0);

    return 0;
}

static uint32_t channelIdle(void)
{
    uint32_t difsTime = DIFS;
    rfiChannelBusy = 0;

    do {
        if (rfiChannelBusy) {
            return 0;
        }
        utilDelayUs(1);
    } while (--difsTime != 0);

    return 1;
}

void linkrfiInit(uint8_t srcAddr)
{
    // Initialise the addresses
    src_addr=  srcAddr;

    // Using source address to nitialise random number generator.
    srand(srcAddr);
    
    // Initialise data link rfi housekeeping data
    upLayerReceivedFunc = 0;
    linkState = LINK_RFI_WORKING;
    seqSend= (uint8_t) rand();		// don't use 0, make condition not same.
    seqRecv= 0;
    bufOffset = 0;
    rfiDataRdy = 0;
    rfiAckRdy = 0;
    rfiChannelBusy = 0;
    
    // Initialise halrfi.
    halrfiInit();
    halrfiSetIntCallback(dataReceived);
}

void linkrfiSetIntCallback(callback_array func)
{
    upLayerReceivedFunc = func;
}

uint32_t linkrfiSend(uint8_t *data, uint32_t len)
{
	uint8_t buf[DATA_FRAME_SIZE];

	uint32_t errorRetrans = 4;
	uint32_t EW = 32;
	uint32_t errortime;
	uint32_t busyRetrans = 4;
	uint32_t BW = 32;
	uint32_t busytime;
    // not multi-frame data support.
    if (len > DATA_SIZE) {
        return 0;
    }

	memset(buf, 0, DATA_FRAME_SIZE);
    LINK_RFI_FRAME_TYPE_BYTE(buf) = LINK_RFI_DATA;
    LINK_RFI_FRAME_SEQ_BYTE(buf) = seqSend;
    LINK_RFI_FRAME_DES_BYTE(buf) = 0xff;     // broadcast send.
    LINK_RFI_FRAME_SRC_BYTE(buf) = src_addr;
    memcpy(LINK_RFI_FRAME_DATA_POINTER(buf), data, len);
    // calculating CRC.
    //LINK_RFI_FRAME_CRC_BYTE(buf) = makeCRCByte(buf, DATA_FRAME_SIZE -1);

	errorRetrans = 4;
	EW = 32;
    do {
		busyRetrans = 4;
		BW = 32;
        do {
            if (channelIdle()) {
				halrfiTxOn();
                halrfiSend(buf, DATA_FRAME_SIZE);
				halrfiRxOn();
                break;
            }
            else {
                // backoff random time based on DIFS.
                busytime = rand() % BW;
                busytime *= DIFS;
                utilSleepUs(busytime);
                BW <<= 1;
            }
        } while (--busyRetrans != 0);

        if (waitForAck(200)) {
            // sending success.
			++seqSend;
            return len;
        }
        else {
            // backoff random time based on EIFS.
            errortime = rand() % EW;
            errortime *= EIFS;
            utilSleepUs(errortime);
            EW <<= 1;
        }
    } while (--errorRetrans != 0);

    return 0;
}

void linkrfiProcessDuty(void)
{
    static uint32_t oldSeqRecv = 0xffffffff;
    // received data.
    if (rfiDataRdy) {
	    rfiDataRdy = 0;
		bufOffset = 0;
        if (seqRecv != oldSeqRecv) {
            oldSeqRecv = seqRecv;
            upLayerReceivedFunc( (uint8_t*)
                    LINK_RFI_FRAME_DATA_POINTER(receiveBuffer),
                    LINK_RFI_FRAME_DATA_LENGTH(receiveBuffer));
        }
		// reopen receive exchange.
		halrfiRxOn();
    }
}

uint32_t linkrfiIsWorking(void)
{
    return (linkState == LINK_RFI_WORKING);
}

